#include "StdAfx.h"
#include "GridHelper.h"
#include "ColorHelper.h"

CGridHelper::CGridHelper(void)
{
	m_gridSize = CSize(15,15);
	m_borderWidth = 30;
	m_borderHeight = 30;

	m_maxGridRow = 10;
	m_maxGridColumn = 10;

}

CGridHelper::~CGridHelper(void)
{
}

CPoint CGridHelper::GridToPoint(double x, double y)
{
	return GridToPoint(CGrid(x,y));
}

CPoint CGridHelper::GridToPoint(CGrid grid)
{
	CPoint point;

	point.x = round(GetHorizontalBorder() + grid.cx * GetCellSize().cx);
	point.y = round(GetVerticalBorder() + grid.cy * GetCellSize().cy);

	return point;
}

CGrid CGridHelper::PointToGrid(CPoint point)
{
	CGrid grid;

	ASSERT(GetCellSize().cx > 0 && GetCellSize().cy > 0);

	grid.cx = (point.x - GetHorizontalBorder())/GetCellSize().cx;
	grid.cy = (point.y - GetVerticalBorder())/GetCellSize().cy;

	return grid;
}

CRect CGridHelper::GridToRect(CGrid topLeft, CGrid bottomRight)
{
	CRect rect;

	rect.TopLeft() = GridToPoint(topLeft);
	rect.BottomRight() = GridToPoint(bottomRight);

	return rect;
}

CSize CGridHelper::GetCellSize(void)
{
	return m_gridSize;
}

void CGridHelper::SetCellSize(CSize size)
{
	m_gridSize = size;
}

LONG CGridHelper::GetHorizontalBorder(void)
{
	return m_borderWidth;
}

LONG CGridHelper::GetVerticalBorder(void)
{
	return m_borderHeight;
}

int CGridHelper::GetGridColor(void)
{
	return RGB(192, 192, 192);
}

void CGridHelper::DrawGrid(CDC* dc) 
{ 
	COLORREF gridcol = GetGridColor(); 
	CPen pen; 
	pen.CreatePen( PS_SOLID, 1, gridcol ); 
	dc->SelectObject( &pen ); 

	// To avoid accumulating rounding errors, we don't 
	// precalculate the grid size for the given zoom... 
	int stepx = GetGridColumn(); 
	int stepy = GetGridRow(); 

	// ...instead we calculate the position of each line. 
	for( int x = 0 ; x <= stepx ; x++ ) 
	{ 
		dc->MoveTo( GridToPoint(x, 0) ); 
		dc->LineTo( GridToPoint(x, stepy) ); 
	} 

	for( int y = 0; y <= stepy ; y++ ) 
	{ 
		dc->MoveTo( GridToPoint(0, y) ); 
		dc->LineTo( GridToPoint(stepx, y) ); 
	} 

	dc->SelectStockObject( BLACK_PEN );      
} 

void CGridHelper::SetGridSize(LONG row, LONG column)
{

	if (row <= 0 || column <= 0)
	{
		return;
	}

	m_maxGridRow = row;
	m_maxGridColumn = column;
	return;
}

LONG CGridHelper::GetGridRow(void)
{
	ASSERT(m_maxGridRow);
	return m_maxGridRow;
}

long CGridHelper::GetGridColumn(void)
{
	ASSERT(m_maxGridColumn);
	return m_maxGridColumn;
}

CSize CGridHelper::GetVirtualSize(void)
{
	CSize size;

	size.cx = 2 * GetHorizontalBorder() + GetCellSize().cx * GetGridColumn();
	size.cy = 2 * GetVerticalBorder() + GetCellSize().cy * GetGridRow();

	return size;
}

CRect CGridHelper::GetGridRect(void)
{
	CRect rect(GetHorizontalBorder(), GetVerticalBorder(), GetCellSize().cx * GetGridColumn(), GetCellSize().cy * GetGridRow());

	return rect;
}

void CGridHelper::AdjustRectInGrid(CRect &rect)
{
	CRect gridRect = GetGridRect();
	rect.left = max(gridRect.left, rect.left);
	rect.top = max(gridRect.top, rect.top);
	rect.right = min(gridRect.right, rect.right);
	rect.bottom = min(gridRect.bottom, rect.bottom);
}

void CGridHelper::DrawRect(CDC *dc, double x, double y, double cx, double cy, COLORREF color,   bool bHorizontalOrVertical)
{
	DrawRect(dc, CGrid(x, y), CGrid(x+cx, y+cy), color, bHorizontalOrVertical);
}

void CGridHelper::DrawRect(CDC *dc, CGrid topLeft, CGrid bottomRight,COLORREF color,  bool bHorizontalOrVertical)
{
	CRect rect(GridToPoint(topLeft), GridToPoint(bottomRight));

	AdjustRectInGrid(rect);
	if(!rect.Width() || !rect.Height())
	{
		return;
	}


	COLORREF colorG, colorEnd, colorSmooth, colorBorder;
	colorBorder = CColorHelper::DifferColor(color, 100);
	colorEnd = CColorHelper::DifferColor(color, 86);
	colorSmooth = CColorHelper::OrColor(colorBorder, dc->GetBkColor(), 40);

	dc->FillSolidRect(rect, colorBorder);

	if (bHorizontalOrVertical)
	{
		for (int x = 0; x <= rect.Height() - 1; x ++)
		{
			colorG = CColorHelper::OrColor(color, colorEnd, x/rect.Height());
			if (x < rect.Height()/2)
			{
				colorG = CColorHelper::OrColor(colorG, RGB(255,255,255), min(32+3*x, 100));
			}

			dc->FillSolidRect(rect.left, rect.top+x, rect.Width(), 1, colorG);
		}
	}
	else
	{
		for (int x = 0; x <= rect.Width() - 1; x ++)
		{
			colorG = CColorHelper::OrColor(color, colorEnd, x/rect.Width());
			if (x < rect.Width()/2)
			{
				colorG = CColorHelper::OrColor(colorG, RGB(255,255,255), min(32+8*x, 100));
			}

			dc->FillSolidRect(rect.left + x, rect.top, 1, rect.Height(), colorG);
		}
	}
}

void CGridHelper::DrawTriangle(CDC * dc, double x, double y, COLORREF colorBorder, COLORREF colorFill, bool bHorizontalOrVertical)
{
	DrawTriangle(dc, CGrid(x, y), colorBorder, colorFill, bHorizontalOrVertical);
}

void CGridHelper::DrawTriangle(CDC * dc, CGrid grid, COLORREF colorBorder, COLORREF colorFill, bool bHorizontalOrVertical)
{
	if (!PtInGrid(grid))
	{
		return;
	}

	CPoint point[4];
	point[0] = GridToPoint(grid);
	
	if (bHorizontalOrVertical)
	{
		point[1].y = point[2].y = point[0].y - GetCellSize().cy;
		point[1].x = point[0].x - round(0.5 * GetCellSize().cx); //0.86602540378443864676372317075294
		point[2].x = point[0].x + round(0.5 * GetCellSize().cx);
		point[3].x = point[0].x;
		point[3].y = point[0].y + GetCellSize().cy;
	}
	else
	{
		point[1].x = point[2].x  = point[0].x - GetCellSize().cx;
		point[1].y = point[0].y - round(0.5 * GetCellSize().cy);
		point[2].y = point[0].y + round(0.5 * GetCellSize().cy);
		point[3].x = point[0].x + GetCellSize().cx;
		point[3].y = point[0].y;
	}

	CPen pen; 
	CRgn rgn;
	CBrush brush;

	rgn.CreatePolygonRgn(point, 3, WINDING);
	brush.CreateSolidBrush(colorFill);
	dc->FillRgn(&rgn, &brush);

	pen.CreatePen( PS_SOLID, 2, colorBorder ); 
	dc->SelectObject( &pen ); 
	dc->MoveTo(point[0]);
	dc->LineTo(point[1]);
	dc->LineTo(point[2]);
	dc->LineTo(point[0]);
	dc->LineTo(point[3]);

	dc->SelectStockObject( BLACK_PEN );      
}

BOOL CGridHelper::PtInGrid(CGrid grid)
{
	BOOL bRes = TRUE;
	if (grid.cx < 0 || grid.cx > GetGridColumn() || grid.cy < 0 || grid.cy > GetGridRow())
	{
		bRes = FALSE;
	}
	return bRes;
}

void CGridHelper::DrawLink(CDC*dc, double x1, double y1, double x2, double y2, COLORREF color, int nWidth)
{
	DrawLink(dc, CGrid(x1, y1), CGrid(x2, y2), color, nWidth);
}

void CGridHelper::DrawLink(CDC*dc, CGrid firstGrid, CGrid secondGrid, COLORREF color, int nWidth)
{
	CPen pen1, pen2; 
	CPoint pt1, pt2, pt3, pt4;
	double slope, dDistance, dPercent;

	if (nWidth < 1) nWidth = 1;

	pt1 = GridToPoint(firstGrid);
	pt2 = GridToPoint(secondGrid);
	dDistance = LineDistance(pt1, pt2);
	if (dDistance <= 0)
	{
		return;
	}

	//Draw dot line with the width 2
	if (pt1.x == pt2.x)
	{
		slope = 1.5707963267948966192313216916398; //PI/2
	}
	else
	{
		slope = atan2(double(pt2.y - pt1.y), double(pt2.x - pt1.x));
	}

	pen1.CreatePen( PS_SOLID, nWidth, color ); 
	dc->SelectObject( &pen1 ); 

	int nDotLineLength = round(GetCellSize().cx*3/4);
	pt3 = pt1;
	for (dPercent = 1.0 * nDotLineLength / dDistance; dPercent <= 1; dPercent += nDotLineLength / dDistance)
	{
		dc->MoveTo(pt3);
		if (dPercent > 1)
		{
			pt4 = pt2;
		}
		pt4 = LinePoint(pt1, pt2, dPercent);
		dc->LineTo(pt4);

		dPercent += 3.0 * nWidth / dDistance;
		pt3 = LinePoint(pt1, pt2, dPercent);
	}

	//Draw arrow at the of line
	pen2.CreatePen( PS_SOLID, nWidth, color ); 
	dc->SelectObject( &pen2 ); 
	//PI/6 = 0.52359877559829887307710723054658 
	int nArrowLength = round(GetCellSize().cx * 3/4);
	pt3.x = round(pt2.x - nArrowLength * cos(slope + 0.52359877559829887307710723054658));
	pt3.y = round(pt2.y - nArrowLength * sin(slope + 0.52359877559829887307710723054658));
	pt4.x = round(pt2.x - nArrowLength * cos(slope - 0.52359877559829887307710723054658));
	pt4.y = round(pt2.y - nArrowLength * sin(slope - 0.52359877559829887307710723054658));
	dc->MoveTo(pt3);
	dc->LineTo(pt2);
	dc->LineTo(pt4);

	dc->SelectStockObject( BLACK_PEN );      
}

double CGridHelper::LineDistance(CPoint pt1, CPoint pt2)
{
	double distance = 0;
	CSize size = pt1 - pt2;
	distance = sqrt(double(size.cx * size.cx + size.cy * size.cy));
	return distance;
}

CPoint CGridHelper::LinePoint(CPoint pt1, CPoint pt2, double dPercent)
{
	CPoint pt;
	pt.x = round(pt1.x + (pt2.x - pt1.x) * dPercent);
	pt.y = round(pt1.y + (pt2.y - pt1.y) * dPercent);
	return pt;
}
